<?php

	/**
	 * Class for Image Processing.
	 * @author Rashid
	 */
	class Image {

		/**
		 * Singleton image instance.
		 */
		private static $_instances = false;

		/**
		 * Location at where images will be store on Amazon S3.
		 * @var string
		 */
		private $_s3ImagePathFolder = "";

		/**
		 * Maximum width of the image.
		 * @var integer
		 */
		private $_maxWidth = 990;
		private $_amazonId = '';
		private $_amazonSecret = '';
		private $_amazonBucket = '';

		/**
		 * Create new instance of Image class.
		 * @param $s3Location, Location at where images will be store on Amazon S3.
		 * @return Image
		 */
		private function __construct( $amazonId, $amazonSecret, $amazonBucket, $s3Folder = false ) {
			require_once( FRAMEWORK_PATH . implode( DIRECTORY_SEPARATOR, array( 'phpthumb', 'ThumbLib.inc.php' ) ) );
			$this->_amazonId = $amazonId;
			$this->_amazonSecret = $amazonSecret;
			$this->_amazonBucket = $amazonBucket;
			if( $s3Folder === false ) {
				$this->_s3ImagePathFolder = $this->_amazonBucket . S3_IMAGE_BUCKET . implode('/', array(date('Y'), date('m')));
			} else {
				$this->_s3ImagePathFolder = $this->_amazonBucket . trim( $s3Folder, '/' );
			}
		}

		private function el_crypto_hmacSHA1( $key, $data, $blocksize = 64 ) {
			if( strlen( $key ) > $blocksize ) {
				$key = pack( 'H*', sha1( $key ) );
			}
			$key = str_pad( $key, $blocksize, chr( 0x00 ) );
			$ipad = str_repeat( chr( 0x36 ), $blocksize );
			$opad = str_repeat( chr( 0x5c ), $blocksize );
			$hmac = pack( 'H*', sha1( ( $key ^ $opad ) . pack( 'H*', sha1( ( $key ^ $ipad ) . $data ) ) ) );
			return base64_encode( $hmac );
		}

		public function getTemporaryLink( $path, $expires = 5 ) {
			$accessKey = $this->_amazonId;
			$secretKey = $this->_amazonSecret;
			$bucket = rtrim( $this->_amazonBucket, '/' );
			$expires = time() + intval( floatval( $expires ) * 60 );
			$path = str_replace( '%2F', '/', rawurlencode( $path = ltrim( $path, '/' ) ) );
			$signpath = '/'. $bucket .'/'. $path;
			$signsz = implode( "\n", $pieces = array( 'GET', null, null, $expires, $signpath ) );
			$signature = $this->el_crypto_hmacSHA1( $secretKey, $signsz );
			$url = sprintf( 'http://%s.s3.amazonaws.com/%s', $bucket, $path );
			$qs = http_build_query( $pieces = array(
				'AWSAccessKeyId' => $accessKey,
				'Expires' => $expires,
				'Signature' => $signature
			));
			return $url . '?' . $qs;
		}

		/**
		 * Returns singlton image class object.
		 * @param string $s3Location
		 */
		public static function getInstance( $amazonId, $amazonSecret, $amazonBucket, $s3Folder = false ) {
			if( !isset( self::$_instances[ $amazonId . $amazonSecret . $amazonBucket ] ) ) {
				self::$_instances[ $amazonId . $amazonSecret . $amazonBucket ] = new self( $amazonId, $amazonSecret, $amazonBucket, $s3Folder );
			}
			return self::$_instances[ $amazonId . $amazonSecret . $amazonBucket ];
		}

		/**
		 * Crops image according to specified width and height.
		 * @param $width, Width of cropped image.
		 * @param $height, Height of cropped image.
		 * @param $sourceFile, Path of file which will be cropped.
		 * @param $targetImageName string, Name of the cropped image.
		 * @param $ignoreAspectRatio boolean, Optional, If true then aspect ration is ignored.
		 * @return string, Name of the generated file.
		 */
		public function resizeImage( $width, $height, $sourceFile, $ignoreAspectRatio = false ) {
			$thumb = PhpThumbFactory::create($sourceFile, array('resizeUp' => true));
			$ignoreAspectRatio ? $thumb->adaptiveResize($width, $height) : $thumb->resize($width, $height);
			$newLocation = $this->_getTempFileName();
			$thumb->save($newLocation);
			return $newLocation;
		}

		/**
		 * This function copies an image file to S3 location.
		 * @param $sourceFilePath, String, Full path of the image file to be copied.
		 * @param $fileName, String, Name using which the file will be stored in s3 folder.
		 * @return boolean, True if operation was successful, False otherwise.
		 */
		public function saveFileOnS3( $sourceFilePath, $fileName, $path = false, $access = 'public-read' ) {
			$contents = file_get_contents( $sourceFilePath );

			$imageFileExtension = explode('.', $fileName);
			$imageFileExtension = $imageFileExtension[count($imageFileExtension) - 1];

			$mimeType = explode( ';', mime_content_type( $sourceFilePath ) );
			$mimeType = $mimeType[ 0 ];

			$path = rtrim( $path ? $this->_amazonBucket . $path : $this->_s3ImagePathFolder, '/' );

			$objS3 = new Amazon_S3(array('keyId' => $this->_amazonId, 'secretKey' => $this->_amazonSecret));
			
			$saved = $objS3->putObject( $fileName, $contents, $path, $access, $mimeType);
			if( is_array( $saved ) && count( $saved ) > 0) {
				if( isset( $saved['etag'] ) ){
					return str_replace( $this->_amazonBucket, '', $path . '/' . $fileName );
				}
			}
			return false;
		}
		
		public function deleteFileFromS3( $key ) {
			$objS3 = new Amazon_S3(array('keyId' => $this->_amazonId, 'secretKey' => $this->_amazonSecret));
			$result = $objS3->deleteObject( $key, rtrim( $this->_amazonBucket, '/' ) );
			return false;
		}
		
		public function downloadPrivateFile( $filePath, $extension='jpg', $bucket='' ) {
			try{
				$objS3 = new Amazon_S3(array( 'keyId' => $this->_amazonId, 'secretKey' => $this->_amazonSecret ) );
				
				$currentBucket = $bucket != ''? $bucket : $this->_amazonBucket;
				$result = $objS3->getObject( $filePath, rtrim( $currentBucket, '/' ) );
				
				if( is_array( $result ) ){
					throw new Exception( "Invalid media path" );
				}
				
				$tempFile = PUBLIC_PATH . 'temp_files' . DIRECTORY_SEPARATOR . 'tempfile_' . randomStringGenerator(7, true) . '.' . $extension;
				
				$handle = fopen($tempFile, "w+");
				if($handle)
				{
					fwrite($handle, $result);
					fclose($handle);
				}
				//describe( $currentBucket, $extension, $filePath , $tempFile, true);
				return $tempFile;
			}catch( Exception $ex ){
				throw $ex;
			}
		}		
		
		public function downloadFileLocally( $filePath ){
			$fileName = end( explode( '/', $filePath ) );
			$objS3 = new Amazon_S3(array( 'keyId' => $this->_amazonId, 'secretKey' => $this->_amazonSecret ) );
			$result = $objS3->getObject( $filePath, rtrim( $this->_amazonBucket, '/' ) );
			$tempFile = PUBLIC_PATH . 'temp_files' . DIRECTORY_SEPARATOR . $fileName . '.' . $extension;
			$handle = fopen( $tempFile, "w+" );
			if( $handle ){
				fwrite( $handle, $result);
				header('Content-Description: File Transfer');
				header('Content-Type: application/octet-stream');
				header('Content-Disposition: attachment; filename='.$fileName . '.' . $extension);
				header('Content-Transfer-Encoding: binary');
				header('Expires: 0');
				header('Cache-Control: must-revalidate');
				header('Pragma: public');
				header('Content-Length: ' . filesize($tempFile));
				ob_clean();
				flush();			    
				readfile($tempFile);
				fclose($handle);
				@unlink( $tempFile );
			}
			return false;
		}

		/**
		 * Returns a new temp
		 * @param string $extension
		 * @return string
		 */
		public function _getTempFileName() {
			$tempName = tempnam( sys_get_temp_dir(), 'TFL' );
			chmod( $tempName, 0777 );
			return $tempName;
		}

		public function uploadImageToPath( $url, $path ) {
			$fileName = end( explode( '/', $path ) );
			$path = str_replace( '/' . $fileName, '', $path );
			$file = $this->downloadImage( $url );
			if( $this->saveFileOnS3( $file, $fileName, $path ) ) {
				@unlink( $file );
				return true;
			}
			@unlink( $file );
			return false;
		}

		/**
		 * Uploads an image from client file system and then stores that file in the s3 location.
		 * @param $fileInfo, Array, An array containing info of $_FILES element.
		 * @param $imageTitle, String, Optional, If not specified then a name is generated at run time for the image otherwise the specified string is used.
		 * @return String, S3 location of the stored file.
		 */
		public function uploadImageFromFile($fileInfo, $imageTitle = '', $index = 0, $process = true, $imageSlug = false ) {
			if (is_array($fileInfo['type'])) {
				$fileInfo = array(
					'name' => $fileInfo['name'][$index],
					'type' => $fileInfo['type'][$index],
					'tmp_name' => $fileInfo['tmp_name'][$index],
					'error' => $fileInfo['error'][$index],
					'size' => $fileInfo['size'][$index]
				);
			}

			$fileInfo[ 'type' ] = explode( ';', $fileInfo[ 'type' ] );
			$fileInfo[ 'type' ] = $fileInfo[ 'type' ][ 0 ];

			if ($fileInfo['error'] !== UPLOAD_ERR_OK) {
				throw new Exception(self::getUploadErrorMessage($fileInfo['error']));
			} else if (array_search($fileInfo['type'], array('image/pjpeg', 'image/jpeg', 'image/png', 'image/gif', 'image/bmp')) === false) {
				throw new Exception('Unsuported file type "' . $fileInfo['type'] . '"!');
			}

			$extension = explode("/", $fileInfo['type']);
			$extension = $extension[1];

			if (array_search($extension, array('pjpeg', 'jpeg')) !== false) {
				$extension = "jpg";
			}

			if( $imageSlug === false ) {
				if (!$imageTitle) {
					$imageTitle = explode('.', $fileInfo['name']);
					$imageTitle = ucwords(str_replace('-', ' ', $imageTitle[0]));
				}

				$imageSlug = str_replace('-', '_', sanitizeWithDashes($imageTitle)) . '_' . randomStringGenerator(5, true);
			}

			$filePath = $fileInfo['tmp_name'];
			if (is_uploaded_file($fileInfo['tmp_name'])) {
				$filePath = $this->_getTempFileName($extension);
				@move_uploaded_file($fileInfo['tmp_name'], $filePath);
			}

			$thumb = PhpThumbFactory::create($filePath, array('resizeUp' => true));
			$imageDimension = $thumb->getCurrentDimensions();

			$result = array();
			if (( $imageDimension['width'] > $this->_maxWidth ) && $process) {
				$cropImageHeight = round(( $this->_maxWidth / $imageDimension['width'] ) * $imageDimension['height']);
				$thumb->resize($this->_maxWidth, $cropImageHeight);
				$newLocation = $this->_getTempFileName();
				$thumb->save($newLocation);
				if ($amazonFileUri = $this->saveFileOnS3($newLocation, $imageSlug . "." . $extension)) {
					$result['image'] = array(
						'uri' => $amazonFileUri,
						'width' => $this->_maxWidth,
						'height' => $cropImageHeight
					);
					$result['original'] = array(
						'uri' => $this->saveFileOnS3($filePath, $imageSlug . "_original." . $extension),
						'width' => $imageDimension['width'],
						'height' => $imageDimension['height']
					);
				}
				@unlink($newLocation);
			} else {
				$result['image'] = array(
					'uri' => $this->saveFileOnS3($filePath, $imageSlug . "." . $extension),
					'width' => $imageDimension['width'],
					'height' => $imageDimension['height']
				);
			}
			if (file_exists($filePath)) {
				@unlink($filePath);
			}
			return $result;
		}

		/**
		 * This function downloads an url to file system and then uploads that image to S3 location.
		 * @param $imageUrl, String, URL of the image that has to be downloaded.
		 * @param $imageTitle, String, Optional, If not specified then a name is generated at run time for the image otherwise the specified string is used.
		 * @return String, Path of the image.
		 */
		public function uploadImageFromUrl($imageUrl, $imageTitle = '', $process = true, $imageSlug = false ) {
			if (!@parse_url($imageUrl)) {
				throw new Exception("'$imageUrl' does not seem to be a valid url!");
			}
			if (!@getimagesize($imageUrl)) {
				throw new Exception("'$imageUrl' is not a valid image url or not accessible right now!");
			}
			if( $tempLocation = self::downloadImage( $imageUrl ) ) {
				$imageName = explode(DIRECTORY_SEPARATOR, $tempLocation);
				$result = $this->uploadImageFromFile( array(
					'name' => $imageName[count($imageName) - 1],
					'type' => mime_content_type($tempLocation),
					'tmp_name' => $tempLocation,
					'error' => 0,
					'size' => filesize($tempLocation)
						), $imageTitle, 0, $process, $imageSlug );
				@unlink( $tempLocation );
				return $result;
			} else {
				@unlink( $tempLocation );
				throw new Exception("'$imageUrl' could not be saved locally to '$tempLocation'!");
			}
		}

		public function uploadImageFromS3PrivateUrl($imageUrl, $imageTitle = '', $process = true, $imageSlug = false ) {
			try{
				if( $tempLocation = self::downloadPrivateFile( $imageUrl, 'jpg' ) ) {
					$imageName = explode(DIRECTORY_SEPARATOR, $tempLocation);
					$result = $this->uploadImageFromFile( array(
						'name' => $imageName[count($imageName) - 1],
						'type' => mime_content_type($tempLocation),
						'tmp_name' => $tempLocation,
						'error' => 0,
						'size' => filesize($tempLocation)
							), $imageTitle, 0, $process, $imageSlug );
					@unlink( $tempLocation );
					return $result;
				}
				throw new Exception("Unable to upload file on s3");
			}catch( Exception $ex ){
				throw $ex;
			}
		}

		/**
		 * Downloads image from remote server to local.
		 * @param string $url
		 */
		public static function downloadImage( $url ) {
			$arrFilePath = pathinfo( $url );
			$urlNewPath = $arrFilePath[ 'dirname' ] . '/' . urlencode( $arrFilePath[ 'filename' ] ) . '.' . $arrFilePath[ 'extension' ];
			
			$ch = curl_init( $urlNewPath );
			curl_setopt( $ch, CURLOPT_HEADER, 0 );
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, 1 );
			curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );
			curl_setopt( $ch, CURLOPT_FAILONERROR, 1 );
			curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0');
			$rawdata = curl_exec( $ch );
			if( curl_getinfo( $ch, CURLINFO_HTTP_CODE ) == 200 ) {
				curl_close( $ch );
				$fullpath = self::_getTempFileName();
				$fp = fopen( $fullpath, 'w' );
				fwrite( $fp, $rawdata );
				fclose( $fp );
				return $fullpath;
			}
			curl_close( $ch );
			return false;
		}

		/**
		 * Crops an image with specified area.
		 * @param string $filePath
		 * @param number $xCord
		 * @param number $yCord
		 * @param number $targetWidth
		 * @param number $targetHeight
		 * @throws Exception
		 * @return string
		 */
		public function cropImage($filePath, $xCord = 0, $yCord = 0, $targetWidth = 100, $targetHeight = 100) {
			//print_r( func_get_args() );
			if( !file_exists( $filePath ) ) {
				$filePath = $this->downloadImage( $filePath );
			}
			$types = array('image/gif' => 'gif',
				'image/jpeg' => 'jpeg',
				'image/png' => 'png',
				'image/pjpeg' => 'jpeg',
				'image/bmp' => 'wbmp'
			);
			$finfo = new finfo(FILEINFO_MIME_TYPE);
			$library = $types[$finfo->file($filePath)];
			//$library = $types[mime_content_type($filePath)];

			$function = 'imagecreatefrom' . $library;
			if (!function_exists($function)) {
				throw new Exception("Unable to generate image object of '$library'!");
			}
			$imageObject = $function($filePath);
			$targetImageObject = imagecreatetruecolor($targetWidth, $targetHeight);

			imagecopyresampled($targetImageObject, $imageObject, 0, 0, $xCord, $yCord, $targetWidth, $targetHeight, $targetWidth, $targetHeight);
			$function = 'image' . $library;

			$newLocation = $this->_getTempFileName();
			if($library=='png'){
				$image = $function($targetImageObject, $newLocation, 0);
			}else{
				$image = $function($targetImageObject, $newLocation, 100);
			}
			
			if ($image) {
				imagedestroy($targetImageObject);
				$imageTitle = explode(DIRECTORY_SEPARATOR, $filePath);
				$imageTitle = explode('.', $imageTitle[count($imageTitle) - 1]);
				$imageTitle = str_replace('-', ' ', str_replace('_', ' ', ucfirst($imageTitle[0])));

				$imageSlug = sanitizeWithDashes($imageTitle);
				$imageSlug = str_replace('-', '_', $imageSlug);
				$imageSlug .= '_' . randomStringGenerator(5, true);

				$amazonFileUri = $this->saveFileOnS3($newLocation, $imageSlug . '.' . array_search($library, array('jpg' => 'jpeg', 'bmp' => 'wbmp', 'gif' => 'gif', 'png' => 'png')));
				if ($amazonFileUri) {
					$amazonFileUri = str_replace($this->_amazonBucket . '/images/', '', $amazonFileUri);
					$image = getDb( 'image', 'add' );
					$image->set( 'title||' . $imageTitle, 'uri||' . $amazonFileUri, 'width||' . $targetWidth, 'height||' . $targetHeight );
				} else {
					throw new Exception('Could not store file on Amazon S3!');
				}
				@unlink($newLocation);
				return $amazonFileUri;
			} else {
				@unlink($newLocation);
				throw new Exception('Could not create image object!');
			}
		}

		/**
		 * Returns the error message for upload file error.
		 * @param integer $errorCode
		 */
		public static function getUploadErrorMessage($errorCode) {
			switch ($errorCode) {
				case UPLOAD_ERR_INI_SIZE: {
						return 'The uploaded file exceeds the upload_max_filesize directive! It should not be more than ' . ( ini_get('upload_max_filesize') ) . '.';
					} case UPLOAD_ERR_FORM_SIZE: {
						return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
					} case UPLOAD_ERR_PARTIAL: {
						return 'The uploaded file was only partially uploaded';
					} case UPLOAD_ERR_NO_FILE: {
						return 'No file was uploaded';
					} case UPLOAD_ERR_NO_TMP_DIR: {
						return 'Missing a temporary folder';
					} case UPLOAD_ERR_CANT_WRITE: {
						return 'Failed to write file to disk';
					} case UPLOAD_ERR_EXTENSION: {
						return 'File upload stopped by extension';
					} default: {
						return 'Unknown upload error';
					}
			}
		}
		
		function createbucket($bucketname) {
			$objS3 = new Amazon_S3(array('keyId' => $this->_amazonId, 'secretKey' => $this->_amazonSecret));
			$saved = $objS3->putBucket($bucketname);
			return $saved;
		}
	}

	ini_set( 'gd.jpeg_ignore_warning', 1 );
	if( !function_exists( 'mime_content_type' ) ) {
		function mime_content_type($filename) {
			$finfo = finfo_open(FILEINFO_MIME);
			$mimetype = finfo_file($finfo, $filename);
			finfo_close($finfo);
			return $mimetype;
		}
	}